home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Game-Power
/
Amiga Game-Power.iso
/
power games ii
/
galactic worm
/
corewars
/
anleitung
/
kapiteli
< prev
next >
Wrap
Text File
|
1994-05-20
|
70KB
|
1,512 lines
Kapitel 1: Das Spiel
____________________
1.1 Einleitung
______________
Wohl Niemand könnte das Prinzip des vorliegenden Computerspiels besser
in Worte fassen als sein Erfinder, A. K. Dewdney:
"Zwei Computerprogramme in ihrer natürlichen Umgebung - dem Speicherchip
eines Computers - beschleichen einander von Adresse zu Adresse.
Manchmal sind sie darauf aus, den Gegner auszukundschaften. Manchmal
legen sie einen Sperrgürtel aus numerischen Bomben, und manchmal
kopieren sie sich selbst aus der Gafahrenzone oder halten inne, um
Schäden zu beheben. Das ist das Spiel, das ich "Krieg der Sterne"
nennen möchte. Es ist ganz anders als die meisten anderen
Computerspiele, denn Menschen spielen hier überhaupt nicht mehr mit.
Zwar sind die rivalisierenden Programme natürlich von irgendwem
geschrieben; aber sobald der Kampf einmal tobt, kann der Erfinder eines
Programmes nur mehr hilflos abwarten, ob das Produkt stundenlangen
überlegens und Programmierens siegt oder untergeht. Den Ausgang der
Schlacht entscheidet allein, welches Programm an einer empfindlichen
Stelle getroffen wird. "
(Zitat aus: Artikel "Computer Kurzweil", Spektrum der Wissenschaft,
August 1984, von A. K. Dewdney)
1.2 Einige Begriffe
___________________
Im Verlauf dieser Anleitung werden wir wiederholt einige
Fachbegriffe verwenden, um etwas zu erklären. Es wäre zwar an einigen
Stellen möglich gewesen, die englischen Wörter durch deutsche Synonyme
zu ersetzen, doch Sie werden sehen, dass eine Bezeichnung wie
"Assembler" (von englisch to assemble, = montieren) nach kurzer Zeit
weniger verwirrend ist als das deutsche Wort "Uebersetzer". Sie finden
übrigens im Anhang B der Anleitung eine alphabetisch geordnete
Aufstellung der im Text verwendeten Fachbegriffe.
1.2.1 Programm
--------------
Ein Programm ist eine Folge von Anweisungen, die einem
beliebigen ausführenden Objekt eine bestimmte Handlungsweise
vorschreiben. Ein Fernsehprogramm ist daher im Prinzip das gleiche wie
ein Programm einer Waschmaschine oder eines Computers. Der grösste
Unterschied dieser drei Beispiele besteht in der Art und Weise der
Formulierung des Programmes. Das Fernsehprogramm ist durch Zeitangaben
und Namen der Sendungen, die zu diesen Zeiten ausgestrahlt werden,
formuliert.
Das Programm einer älteren Waschmaschine ist durch die Form der
Steuerscheibe bestimmt, einer Metallscheibe, die an bestimmten Stellen
Einbuchtungen oder Erhebungen besitzt und die sich langsam um die eigene
Achse dreht. Ein Fühler berührt den Rand der Scheibe und löst je nach
dem augenblicklichen Stand der Scheibe bestimmte Kontake aus, die z.B.
Wasser einlassen oder die Trommel in Bewegung setzen. Bei einer
moderneren Maschine übernimmt bereits ein Computer die Aufgabe der
Steuerscheibe.
Somit wären wir beim dritten Beispiel angelangt, beim Computer.
Sehen wir uns als erstes ein Textverarbeitungsprogramm an. Es besteht
aus nichts anderem als Anweisungen in einer besonderen, dem Computer
verständlichen Sprache. Duch diese Sprache weiss der Computer immer,
was er als nächstes tun soll: Eingaben von der Tastatur annehmen, einen
Text ausdrucken, auf eine bestimmte Aktion des Benutzers einen
Buchstaben des Textes löschen. Der grosse Vorteil des Computers
gegenüber einer alten Waschmaschine, die durch die Steuerkurve
"programmiert" wurde, ist nun, dass anstelle eines
Textverarbeitungsprogrammes genausogut ein beliebiges anderes Programm
hätte laufen können. Man sagt, der Computer sei "frei programmierbar".
1.2.2 Hardware und Software
---------------------------
Dies sind die Bezeichnungen für einen Computer, auf dem ein
Programm laufen kann und für das Programm, das auf dem Computer oder
eben der Hardware läuft. Software kann aber auch aus Text oder aus
Daten für eine Datenbank bestehen. Generell kann man beliebige
Informationen als Software bezeichnen. Genauso gilt als Hardware nicht
nur ein Computer, sondern auch die Zusatzgeräte rund um einen Computer,
wie Diskettenlaufwerke, Monitoren, oder Speicherkarten.
1.2.3 Programmiersprache
------------------------
Eine Programmiersprache ist die Art und Weise, einem Computer
eine Folge von Anweisungen beizubringen. Diese Sprachen können entweder
nur aus Zahlen oder aus einer Mischung von verständlichen Wörtern und
Zahlen bestehen. Man unterscheidet hier vor allem zwischen zwei
verschiedenen Sprachgruppen: Maschinensprachen und Hochsprachen. Eine
Hochsprache ist auf den Menschen zugeschnitten und daher leichter zu
lernen und zu bedienen. Eine Maschinensprache ist hingegen auf einen
Computer zugeschnitten, und das bedingt die Anpassung des Menschen an
die Maschine. Maschinensprache ist schnell, aber schwieriger zu
erlernen. Grosse Softwareprojekte werden in der Regel in Hochsprachen
erstellt.
1.2.4 Interpreter
-----------------
Ein Interpreter liest jeweils aus einem Programm in der Hochsprache den
nächsten einzelnen Befehl, übersetzt ihn in die Maschinensprache und
führt diese dann aus. Wenn in einem Programm ein solcher Befehl
mehrmals vorkommt, muss er jedesmal neu übersetzt werden, was
natürlich viel Zeit kostet. Basic ist das Standardbeispiel für eine
Sprache, die während der Ausführung übersetzt wird, und deshalb im
Vergleich mit Compilersprachen sehr langsam ist. Weil zudem eine
Anweisung in einer Hochsprache in mehrere Maschinensprachebefehle
übersetzt werden muss, verstärkt sich die Verlangsmung der
Interpretersprachen zusätzlich.
1.2.5 Compiler
--------------
Der Compiler dagegen nimmt sich gleich zu Beginn das ganze
Programm in der Hochsprache vor und übersetzt es in ein vollständiges
Maschinenspracheprogramm. Er montiert sozusagen die einzelnen Befehle
des Programmes zu einem grossen, dem Computer verständl ichen
Maschinenspracheprogramm, das später in einem Stück läuft.
Pascal oder C sind Beispiele für Sprachen, die vor dem Starten des
Programmes zuerst vollständig in den Maschinencode übersetzt und
erst dann vom Computer ausgeführt werden. Das bringt zwar schnelle
Programme, auf der anderen Seite jedoch eine langwierige Fehlersuche.
Wenn auch nur eine winzige Kleinigkeit eines solchen Programmes
geändert werden soll, muss der Programmtext nach der Korrektur ganz
oder teilweise neu übersetzt (= compiliert) werden.
1.2.6 Terminal
--------------
Es handelt sich hier um einen Sammelbegriff für Geräte, die die
Kommunikation zwischen Computer und Mensch möglich machen. Ein Terminal
kann ein Monitor, ein Drucker oder eine Tastatur sein, aber auch ein
Computer, der die Ein- und Ausgaben für ein gröss eres, vernetztes
(zusammengeschaltetes) Computersystem übernimmt, sein.
1.2.7 Bit und Byte
-------------------
Ein Bit ist die kleinste Speichereinheit eines Computers. Ein
solches Bit kann nur zwei Zustände annehmen: 0 oder 1. In der
Computertechnologie werden 8 dieser Bits zu einem Byte zusammengefasst.
Diese 8 Bits können dann als Ganzes insgesamt 256 verschiedene Zustände
annehmen. Diese Zustände werden durch die Zahlen von 0 bis 255
beschrieben.
Der gesamte Speicher eines Computers besteht ausschliesslich aus
sehr vielen einzelnen Bytes. Die Bytes werden von 0 beginnend
durchnummeriert, das heisst, jedem einzelnen Byte im Speicher wird eine
eigene Nummer oder Adresse zugewiesen. So kann auf ein beliebiges Byte
im Speicher durch seine Adresse zugegriffen werden.
Wenn man über Computer spricht, werden unweigerlich die Begriffe KByte
oder MByte fallen. Ein KByte ist ganz einfach ein 'Kilobyte' (wird auch
so ausgesprochen) und bedeutet 1024 Bytes. Ein MByte ist
dementsprechend ein 'Megabyte' und umfasst 1024 mal 1024 Bytes, also
1'048'576 Bytes. Die Gross- und Kleinschreibung bei diesen Begriffen
muss beachtet werden: Ein kByte und ein mByte sind im Gegensatz zum
KByte und zum MByte nur 1000, bzw. 1000000 Bytes gross.
1.2.8 Assembler
---------------
Ein Assembler ist ein Programm, das einen Programmtext, der in
einer Assemblersprache geschrieben wurde, in den ausführbaren
Maschinencode übersetzt. Im Prinzip ist eine Assemblersprache und
Maschinensprache genau dasselbe, der Unterschied besteht einzig
und allein darin, dass eine Assemblersprache die Befehle der
Maschinensprache anstatt in Zahlen in Buchstaben akzeptiert. Für jeden
Befehl in Maschinensprache steht ein Wort, das die Bedeutung des Befehls
ungefähr umschreibt. Da man sich Wörter viel leichter merken kann als
nackte Zahlen, wird Maschinensprache heutzutage vorwiegend mit Hilfe
eines Assemblers programmiert. Es ist wichtig, dass man den Unterschied
zwischen einem Assembler und einer Assemblersprache kennt: Der Assembler
übersetzt die Assemblersprache in den entsprechenden Maschinencode.
1.2.9 Simulation
----------------
Der Computer eignet sich hervorragend dazu, Teile der wirklichen
Welt im Zahlenmodell nachzuspielen oder zu simulieren. Alles kann
simuliert werden; der Erfolg der Simulation hängt wesentlich davon ab,
wie gut der Programmierer das zu simulierende Problem verstanden hat.
Da die Ereignisse in der Realität oft von Tausenden von Variablen
abhängen, sind viele Dinge nur schwer wirkungsvoll zu simulieren. Es
gibt aber etliche andere Probleme, die sehr gut simuliert werden können.
Aus zwei Gründen sind Simulationen wichtig: Einmal kann man die
Parameter eines Problems variieren und ihren Einfluss auf das Ergebnis
beobachten, auch wenn derartige Experimente im wirklichen Leben zu
kostspielig oder zu gefährlich sind. Zum zweiten kann man in der
Simulation Situationen schaffen, die in der Realität nicht auftreten
können.
1.2.10 Emulator
---------------
Ein Emulator oder eine Emulation ist ein Sonderfall einer
Simulation. Wenn auf einem beliebigen Computer das Verhalten eines
anderen Computers simuliert wird, so spricht man von einer Emulation.
Für den Amiga gibt es eine IBM-PC Emulation, die rein softwaremässig
funktioniert. Erfahrungsgemäss sind Simulationen eines anderen
Computers nicht so leistungfähig wie das Vorbild, doch meistens reicht
es, um einfache Anwendungen ausführen zu können. Eine hardwaremässige
Emulation eines IBM-PC ist mit dem Sidecar möglich, einem Zusatzgerät,
das an den Amiga 1000 angeschlossen wird und ihn in einen PC umwandelt.
Das später in dieser Anleitung noch genau erklärte Computersystem MARS
wird auf dem Amiga nur durch Software emuliert.
1.3 Geschichte
______________
Alles begann mit einem Artikel in der Ausgabe der Zeitschrift
"Spektrum der Wissenschaft" vom April 1984. Der amerikanische Autor von
Computer-Kurzweil, A. K. Dewdney stellte die Idee eines Spieles vor,
in dem sich zwei Programme in einem Computer auf Leben und Tod
bekämpfen. Der Sinn des Spieles war es, das gegnerische Programm aus
dem Speicher zu löschen. Die Programme sollten in einer speziellen, nur
für diesen Zweck entwickelten Sprache geschrieben sein. Diesem Artikel
folgten in unregelmässigen Zeitabständen weitere über dasselbe Thema,
und in diesen Artikeln erweiterte A. K. Dewdney seine Spielidee mit
neuen Befehlen seiner Programmiersprache und neuen Gedanken zum Aufbau
solcher Kampfprogramme. Mit der Zeit verbreitete sich das Spiel, und
bald wurden erste Turniere ausgetragen. Schliesslich wurde die
Internationale Gesellschaft für Core Wars gegründet und legte ein
offizielles Regelwerk vor. Auf der Basis dieser "Core War Standards"
wurde das hier vorgestellte Programm entwickelt.
1.4 Core Wars
_____________
1.4.1 Der Name "Core Wars"
--------------------------
Der Name "Core Wars" bedeutet soviel wie "Krieg der Kerne". Diese
Bezeichnung ist von einer veralteten Speicher-Technologie abgeleitet.
In den fünfziger und sechziger Jahren wurden Computerspeicher aus
Tausenden von kleinen, kreisrunden Ringen aus magnetischem Material
aufgebaut, die an einem Netzwerk aus feinen Drähten aufgereiht waren.
Jeder Ring (Durchmesser kleiner als 1 mm) konnte in zwei Richtungen
magnetisiert werden, was den beiden Werten 0 und 1 eines Bits entsprach.
Diese Datenspeicher wurden Kernspeicher genannt, weil die kleinen Ringe
gewissermassen die Speicherkerne des Computers darstellten. Die
Kernspeicher waren zu ihrer Zeit Wunder der Feinmechanik: Auf einer
Fläche von etwa 50 mal 50 Millimetern konnten schon 4096 Bits oder 512
Bytes ge speichert werden. Doch das war vor etwa 20 Jahren: Heute
finden auf der gleichen Fläche rund 15'000 mal mehr Informationen
Platz! In der Computertechnologie wird die CPU mit dem
Schreib-Lesespeicher (RAM) deshalb auch oft als Kern bezeichnet.
1.4.2 Die Sprache der Kampfprogramme
------------------------------------
Die beiden Kampfprogramme für den "Krieg der Kerne" sind in einer
speziellen Sprache geschrieben, die zur Klasse der Assembler gehört:
Der Redcode. Jede Anweisung eines Redcode-Programmes entspricht einem
Befehl des imaginären Computers, in dem der Kampf stattfindet. Mit
Assemblersprachen wird vergleichsweise wenig gearbeitet, weil die
Programme schwieriger zu verstehen und zu ändern sind,
als entspechende Programme in einer Hochsprache wie Pascal oder Basic.
Es gibt allerdings einige Aufgaben, für die sich Assemblersprachen
gut eignen. Wenn ein Programm so wenig Platz wie möglich belegen oder
so schnell wie möglich laufen soll, wird es normalerweise in einer
Assemblersprache geschrieben.
In Assemblersprachen lassen sich überdies einige Dinge machen,
die in einer höheren Programmiersprache so gut wie unmöglich sind. So
kann man beispielsweise ein Assembler-Programm dazu bringen, seine
eigenen Befehle abzuändern, sich selbst zu kopieren und die Ausführung
an einer völlig anderen Stelle im Speicher fortzusetzen.
1.4.3 Das System "Core Wars"
----------------------------
Core Wars besteht aus vier Hauptbestandteilen: Ein Speicherfeld, in dem
der Kampf stattfinden kann, die Assemblersprache Redcode, einen
imaginären Computer namens MARS (eine Abkürzung von Memory Array Redcode
Simulator und einem Satz von wettstreitenden Kampfprogrammen.
1.4.3.1 Das Speicherfeld
Der Speicher, in dem sich die zwei Programme bekämpfen, besteht
aus 8000 Adressen. Jede Adresse kann eine Redcode-Anweisung mit allen
ihren Parametern aufnehmen. Das ganze Speicherfeld ist in sich
geschlossen, es besteht aus einer Folge von Adressen von 0 bis 7999.
Als nächste Adresse nach 7999 folgt wieder die Adresse 0. MARS
reduziert alle Adressen, die grösser als 7999 sind, indem es den Rest
nach der Division durch 8000 nimmt. Die Zahl 9378 wird also als Adresse
1378 interpretiert. Stellen Sie sich das Speicherfeld von MARS wie
einen grossen Ring aus 8000 Segmenten vor. In jedem Segment findet eine
Anweisung Platz, die dem Kampfprogramm sagt, was es als nächstes tun
soll.
1.4.3.2 Redcode
Die Assemblersprache Redcode, in der die Kampfprogramme
geschrieben werden, besteht aus insgesamt 10 Befehlen. Jeder Befehl hat
eine bestimmte Wirkung auf die Inhalte von beliebigen Adressen im
Speicherfeld von Core Wars. Es gibt Anweisungen, den Inhalt von einer
Adresse auf eine andere zu übertragen, die Inhalte arithmetisch zu
verändern und innerhalb des Programmes vorwärts und rückwärts zu
springen. Die Redcode-Befehle verlangen oft Argumente, die z. B. auf
bestimmte Speicherzellen hinweisen oder Zahlenwerte für arithmetische
Operationen darstellen.
1.4.3.3 MARS und Assembler
MARS ist der Name des Systems, in dem die Kampfprogramme laufen
können. MARS existiert nicht in Wirklichkeit, sondern wird durch das
Programm "Core Wars" (nicht zu verwechseln mit Redcode) auf dem Amiga
simuliert. Der Assembler übersetzt jede Anweisung eines
Redcode-Kampfprogrammes in einen Zahlencode, der dann im
Speicherfeld von MARS abgelegt wird. Jede Adresse im Speicherfeld kann
eine solche Zahl aufnehmen, mit anderen Worten kann jede einzelne der
8000 Zellen des Speicherfeldes eine vollständige Redcode-Anweisung
aufnehmen. Der Abschnitt 1.4 enthält die Redcode-Anweisungen mit
ihren Bedeutungen. Die Buchstaben A und B stehen für die Argumente
(Zahlenwerte), die die betreffende Anweisung benötigt. Der
Redcode-Programmierer muss bei jeder Anweisung mindestens ein Argument
angeben, meistens sogar deren zwei.
1.4.4 Die Grundregeln des Spiels
--------------------------------
Zwei Kampfprogramme werden von MARS in das Speicherfeld auf zufällig
ausgewählte Plätze geladen. Kein Programm weiss, wo im Speicher das
andere sitzt. Die einzige Regel beim Auswählen der Plätze ist, dass
zum Zeitpunkt des Kampfbeginns zwischen den Programmen mindestens 1000
freie Speicherzellen vorhanden sein müssen. Dann werden die Programme
von MARS abwechslungsweise ausgeführt: Ein einzelner Befehl vom ersten
Programm wird abgearbeitet, dann ein einzelner Befehl des zweiten
Programmes, und so weiter.
Was ein Kampfprogramm während des ihm zustehenden
Ausführungszyklus tut, bleibt ganz dem Programmierer überlassen. Es ist
natürlich bestrebt, das andere Programm zu zerstören, indem es dessen
Befehle löscht. Möglich ist aber auch eine Defensivstrategie: Ein
Programm kann sich darauf verlegen, erlittene Schäden zu reparieren oder
auszuweichen, wenn es angegriffen wird.
Die Schlacht endet, wenn MARS auf einen nicht ausführbaren Befehl
stösst. Das Programm mit der fehlerhaften Anweisung - vermutlich
eine vom Gegner gelöschte Speicherzelle - wird zum Verlierer
erklärt. Diese letzte Regel ist hier vorerst etwas vereinfacht
dargestellt worden, weil sich durch einen bestimmten Redcode-Befehl die
Programmausführung auf mehrere, voneinander unabhängige
Einzelprogramme verteilen lässt. Doch mehr dazu im nächsten
Abschnitt, in dem die verschiedenen Redcode-Befehle erklärt werden.
1.5 Die Redcode-Befehle
_______________________
1.5.1 Die Befehlstabelle
------------------------
Anweisung Argumente Kurzbeschreibung
MOV A, B Uebertragen: Uebertrage den Inhalt von
Adresse A auf Adresse B.
ADD A, B Addieren: Addiere den Inhalt von Adresse A
zu Adresse B und speichere das Resultat in
Adresse B.
SUB A, B Subtrahieren: Ziehe den Inhalt von Adresse A
von Adresse B ab und speichere das Resultat
wieder in Adresse B.
JMP A Springen: Setze die Ausführung des Programmes
um A Zellen weiter vorne oder weiter hinten
im Speicher fort.
JMZ A, B Springen, wenn Null: Setze die Ausführung des
Programmes um A Zellen weiter vorne oder
weiter hinten im Speicher fort, wenn der
Inhalt von Adresse B gleich Null ist.
JMN A, B Springen, wenn nicht Null: Setze die Ausführung
des Programmes um A Zellen weiter vorne oder
weiter hinten im Speicher fort, wenn der Inhalt
von Adresse B ungleich Null ist.
DJN A, B Vermindern und Springen, wenn nicht Null: Ziehe
vom Inhalt von Adresse B eins ab und setze die
Ausführung des Programmes um A Zellen weiter
vorne oder weiter hinten im Speicher fort, wenn
das Resultat ungleich Null ist.
CMP A, B Vergleiche: Vergleiche den Inhalt von Adresse
A mit dem von Adresse B und überspringe die
nächste Anweisung, wenn sie ungleich sind; sonst
führe die nächste Anweisung aus.
SPL A Spalte auf: Spalte die Ausführung des Programmes
auf in die nächstfolgende Anweisung und in die
Anweisung, die A Zellen weiter vorne oder weiter
hinten im Speicher liegt.
DAT B Daten: Nicht ausführbare Anweisung: Enthält
jedoch Zahlenwerte, auf die zugegriffen werden
kann.
1.5.2 Die Argumente
-------------------
Wie schon erwähnt, verlangt jeder Redcode-Befehl mindestens ein
Argument. Diese Argumente sind Zahlenwerte, die die Befehle näher
beschreiben. Ein Beispiel: In der Anweisung 'JMP -7' ist nur ein
Argument angegeben, weil dieser Befehl zur Ausführung auch nur einen
Wert benötigt. Die Anweisung bedeutet für MARS, innerhalb des
Programmes um sieben Adressen zurückzuspringen, da das Argument negativ
ist. Stände die Anweisung 'JMP -7' zufällig an der Adresse 3715 im
Speicherfeld, so würde die Ausführung des Programmes mit dem Befehl in
der Speicherzelle 3715 - 7 = 3708 fortgesetzt.
Diese Methode, eine Position im Speicherfeld zu berechnen, wird
relative Adressierung genannt. Als Adressierung wird der Weg
bezeichnet, durch den ein System den Wert einer Speicherzelle
herausfindet, die es bearbeiten soll. In der Sprache Redcode wird nur
die erwähnte relative Art der Adressierung verwendet, deshalb hat ein
Kampfprogramm keine Möglichkeit, seine eigene Position im Speicher zu
erfahren. Ein Kampfprogramm kann zwar wild in der Gegend herumspringen,
aber es weiss nie, an welcher Stelle im Speicher es sich im Augenblick
befindet. Da der Speicher wie ein Ring ohne Anfang und Ende aufgebaut
ist, wäre die Angabe einer Position gar nicht sinnvoll, weil die
Adressnummern nur eine Reihenfolge, aber keinen Beginn und kein Ende
festlegen. Ein weiterer Grund für die ausschliessliche Verwendung der
relativen Adressierung ist die Ausführbarkeit der Kampfprogramme an
beliebigen Positionen im Speicherfeld von MARS, die Programme müssen
relokatibel sein.
Relokatibel bedeutet: Wenn ein Programm an verschiedenen Stellen im
Speicher eines Computers ausführbar sein soll, so darf es keine
absoluten, also mit festen Zahlenwerten angegebenen, Sprungadressen
besitzen. Die relative Adressierung, die in Redcode-Programmen
verwendet wird, berücksichtigt diesen Umstand.
1.5.3 Die Programmzeiger
------------------------
Um die Sprache der Kampfprogramme, Redcode, zu lernen, ist es
wichtig, einen Begriff aus der Welt der Programme genau zu kennen: Der
Programmzeiger.
Eingangs des ersten Kapitels wurde beschrieben, was ein Programm für
einen Computer überhaupt ist. Wir sagten, dass ein Programm aus einer
Folge von Anweisungen für den Computer besteht. Dies ist auch in der
Sprache Redcode der Fall: Ein Kampfprogramm besteht aus einer beliebigen
Anzahl von Redcode-Befehlen oder Programmzeilen, die dem System (hier
also MARS) sagen, was gerade zu tun ist: Eine Zahlenbombe werfen, sich
in Sicherheit bringen, und so weiter. Damit nun MARS genau weiss,
welche Anweisung innerhalb des Programmes als Nächstes ausgeführt
werden muss, merkt sich MARS für jedes Kampfprogramm die Adresse der
als letztes bearbeiteten Programmzeile. Um dies etwas plastischer
darzustellen sehen Sie sich bitte das folgende Listing eines
Kampfprogrammes an. Was die einzelnen Redcode-Befehle bedeuten, ist im
Moment noch unwichtig, Sie sollten sich aber den Aufbau der
Programmzeilen einprägen.
(2340) MOV #0, -1
(2341) JMP $-1
(2342) SPL $-2
(2343) MOV $10, $113
(2344) SPL $112
Die Zahlen vor den Redcode-Anweisungen stellen die Adressen der
Speicherzellen dar, in denen die Zeilen gespeichert sind. Diese
Adressen sind für ein Programm völlig ohne Bedeutung und sind
normalerweise auch nicht bekannt. Ein Redcode-Programm wird rein
zufällig in das Speicherfeld gesetzt, dieses Programm könnte
irgendwo anders im Speicher stehen. Hier dienen die Adressen
ausschliesslich dazu, die Bedeutung und Funktion von Programmzeigern zu
erklären.
Zu Beginn der Ausführung des Programmes besitzt der
Programmzeiger den Wert 2340, der Programmzeiger "zeigt" also
gewissermassen auf die erste Anweisung des Kampfprogrammes. Diese
Anweisung wird ausgeführt und der Programmzeiger um eins aufgezählt, so
dass er nun den Wert 2341 besitzt. Weil immer mindestens zwei
Kampfprogramme gegeneinander spielen, wird nun auf die gleiche Weise
eine Zeile des gegnerischen Programmes bearbeitet. Jedes Programm
besitzt natürlich seinen eigenen Programmzeiger, der immer auf die als
nächstes auszuführende Anweisung zeigt.
Jetzt ist wieder unser Kampfprogramm an der Reihe: MARS schaut
sich den Inhalt des Programmzeigers an, der zu unseren Kampfprogramm
gehört. MARS findet dort die Zahl 2341, die auf die zweite Zeile des
Programmes zeigt. Wie schon gehabt, wird nun die Anweisung dieser
Zeile ausgeführt und der Programmzeiger wieder um eins erhöht, so dass
er auf die dritte Zeile unseres Kampfprogrammes weist. Wieder kommt das
gegnerische Programm zum Zuge, und so geht der Kampf weiter, bis alle
Programme eines Spielers zerstört sind und nur noch Redcode-Programme
des Gegners übriggeblieben sind.
Das alles tönt auf den ersten Blick vielleicht etwas
kompliziert, aber durch die Programmzeiger ist zu jedem Zeitpunkt klar
definiert, welche Anweisung von welchem Programm als nächste auszuführen
ist. Um die Arbeitsweise der Redcode-Anweisungen zu verstehen, ist es
wichtig, dass Ihnen der Sinn und Zweck der Programmzeiger geläufig ist -
der Programmzeiger ist in einem gewissen Sinne das Herz eines
Kampfprogrammes.
1.5.4 Genaue Beschreibung der Redcode-Anweisungen
-------------------------------------------------
Die nächsten Abschnitte enthalten zu jedem der zehn Befehle eine
genaue Beschreibung seiner Wirkung auf den Programmzeiger und auf die
Speicherzellen. Wir haben versucht, möglichst viele Befehle durch
Beispiele zu erklären. Wir empfehlen Ihnen auch, diese Beispiele
laufend durch das Core Wars Programm auszuprobieren und sie
gegeneinander kämpfen zu lassen. Wenn Sie eine Idee für ein eigenes
Testprogramm haben, sollten Sie dieses ebenfalls sofort ausprobieren.
Um mit dem Programm Core Wars zurechtzukommen, können Sie in Kapitel 2
unter "Eine erste Anwendung" nachschlagen. Dort wird anhand eines Beispiels
gezeigt, wie Sie ein Kampfprogramm assemblieren und eine Schlacht
zwischen zwei Redcode-Programmen starten und am Bildschirm betrachten
können. Denn auch für MARS und die Sprache Redcode gilt die alte
Wahrheit, dass man Programmieren nur durch Programmieren lernt!
1.5.5 DAT - Daten
-----------------
Die wichtigste Eigenschaft dieser Anweisung ist, das sie
eigentlich gar keine richtige Anweisung darstellt. Wenn nämlich der
Programmzeiger eines Kampfprogrammes auf eine Zeile mit einem DAT-Befehl
stösst, wird der Programmzeiger sofort gelöscht und das betreffende
Programm "stirbt". DAT-Befehle können jedoch auch Bestandteile eines
Programmes sein, sie können Informationen enthalten, die das Programm
während seiner Ausführung benötigt.
Hier ein Beispiel eines DAT-Befehls: "DAT 0". Die
Speicherzelle, in der dieser Befehl gespeichert ist, darf unter keinen
Umständen ausgeführt werden. Wenn dies trotzdem der Fall ist, und
ein Programmzeiger eines Kampfprogrammes in diese Anweisung rennt, wird
der Programmzeiger gelöscht. Die oben angeführte DAT-Zeile kann
aber auch als Munition dienen: Mit dem MOV-Befehl, der gleich erklärt
wird, kann diese Zeile "DAT 0" einer Granate gleich in beliebige
Speicherzellen im ganzen Speicherfeld geworfen werden. Wenn durch eine
solche DAT-Granate ein gegnerisches Programm getroffen wird, und der
gegnerische Programmzeiger arglos die mit dem DAT-Treffer zerstörte
Programmzeile ausführen will, ist es um ihn geschehen.
Wir werden im Laufe der Erklärungen der anderen Redcode-
Anweisungen weitere Anwendungsmöglichkeiten des DAT-Befehls anhand von
Beispielen aufzeigen.
1.5.6 MOV - Uebertragen
-----------------------
Diese Anweisung dient dazu, eine bestimmte Zahl oder den Inhalt einer
der Speicherzellen in eine andere Speicherzelle zu übertragen. Die
Anweisung "MOV 3, 100" (von Englisch move, bewegen) sagt MARS, drei
Schritte oder Adressen im Speicherfeld vorwärts zu gehen und was es dort
findet, in die Zelle 100 Adressen nach dem MOV-Befehl zu kopieren. Der
alte Inhalt dieser Zelle wird durch den neuen Wert überschrieben. Die
Argumente der Anweisung "MOV 3, 100" sind direkt, das heisst, sie werden
als Adressen interpretiert, in denen direkt etwas verändert wird. Um
ein Argument als direkt anzugeben, gibt es zwei Möglichkeiten: zum einen
kann einfach eine Zahl geschrieben werden, zum andern kann der Zahl ein
$-Zeichen vorangestellt werden. Wenn MARS also eine nackte Zahl als
Argument vorfindet, wird sie als direkt angesehen, und das Gleiche gilt
für eine Zahl mit vorangehendem $-Zeichen. Sie werden es bereits
vermutet haben: Es gibt noch weitere Arten, wie ein Argument
interpretiert werden kann.
Erlaubt sind zwei zusätzliche Adressierungsmodi. Wenn einem Argument
ein @-Zeichen voran geht (wir nennen dieses Zeichen seit jeher den
"Klammeraffen"), wird es "indirekt" interpretiert. Bei der
Anweisung "MOV @3, 100" geschieht folgendes: Der Inhalt der
Adresse, die drei Schritte oder Zellen weiter vorne liegt, wird benutzt,
um schliesslich zu einer weiteren Adresse zu kommen, deren Inhalt dann
hundert Schritte weiter nach vorne kopiert wird. Um dies etwas klarer
darzustellen, haben wir hier ein Beispiel für die indirekte
Adressierung:
(2719) MOV @3, 100
(2720) JMP -5
(2721) DAT 50
(2722) DAT -1
(2723)
-
-
-
(2818) -
(2819) MOV 1, 10
(2820) -
Verfolgen wir nun Schritt für Schritt, was geschieht, wenn die Zeile
Nummer 2719 ausgeführt wird. Dort steht der Befehl "MOV", es
sollen also Daten zwischen zwei Speicherzellen übertragen werden. Das
erste Argument "@3" ist durch den Klammeraffen adressiert und gibt den
Ort an, von dem die Daten zu holen sind: Als erstes drei Zellen nach
vorne gehen, was zu Zeile Nummer 2722 führt. In dieser Speicherzelle
steht eine DAT-Anweisung mit dem Argument '-1', und dieser Wert wird nun
als direkt interpretiert. Das bedeutet, dass wir von Zeile 2722 genau
eine Zelle nach hinten gehen müssen, um den wirklichen Wert zu finden,
der durch den MOV-Befehl übertragen werden soll. Wir stehen deshalb
vor der Zeile $2722-1=2721$, und in dieser Zeile steht die Anweisung
"DAT 50".
Das zweite Argument unserer MOV-Anweisung ist 100 und wird, da keine
spezielle Adressierungsart angegeben wird, als direkt interpretiert.
Das Ziel der Verschiebung liegt dadurch 100 Zeilen weiter, also
$2719+100=2819$. Der MOV-Befehl verschiebt schlussendlich den Inhalt
von Zeile 2721 (DAT 50) nach Zeile 2819. Der ursprüngliche
Inhalt dieser Speicherzelle wird durch "DAT 50" überschrieben.
Um anschliessend nicht auf die DAT-Anweisung aufzulaufen und zu sterben,
springt der Programmzähler fünf Schritte zu rück, um dort andere
Anweisungen zu bearbeiten.
Bisher haben Sie die Adressierungsmodi "direkt" mit dem $-Zeichen und
"indirekt" mit dem @-Zeichen kennengelernt. Die angekündigte dritte
Möglichkeit, ein Argument zu schreiben, ist unmittelbar. Diese
Adressierungsart wird mit dem #-Zeichen eingeleitet und ist eigentlich
ganz leicht zu verstehen: "MOV #3, 100" überträgt ganz
einfach den Zahlenwert 3 auf die Speicherzelle, die 100 Zeilen weiter
vorne im Speicher steht. "#" bedeutet, dass die folgende Zahl
unverändert übernommen wird. Es ist bei der Anwendung der
unmittelbaren Adressierung allerdings Vorsicht geboten, denn nicht alle
Befehle können unmittelbare Werte enthalten. Der MOV-Befehl darf z.
B. als zweites Argument nie eine unmittelbare Zahl enthalten, denn das
würde bedeuten, dass ein absoluter Wert in eine mit ihrer
Zeilennummer angegebenen Speicherzelle übertragen werden kann - und
dies ist nicht gestattet, weil ein Redcode-Kampfprogramm keine
Möglichkeit haben darf, seine eigene Position im Speicher zu erfahren.
Das gleiche gilt für die Sprungbefehle, doch dazu später noch mehr.
Nach den verschiedenen Adressierungsarten und den ersten beiden
Befehlen, "DAT" und "MOV", ist es an der Zeit, die graue Theorie
in ein erstes Beispielprogramm umzusetzen.
1.5.6.1 Knirps
MOV 0, 1
Knirps ist das einfachste Beispiel eines Redcode-Programmes, das sich
selbst im Speicherfeld fortpflanzen kann. Es kopiert den Inhalt der
relativen Adresse 0 in die relative Adresse 1, also die nächstfolgende
Adresse. Es stellt sich nun die Frage, was in der relativen Adresse 0
überhaupt enthalten ist - dort steht nichts anderes als die
Anweisung "MOV 0, 1" selbst! Knirps kopiert sich deshalb bei
jeder Ausführung in die nächstfolgende Speicherzelle. Der
Programmzeiger wird nach dem Ausführen des Befehls um eins erhöht,
zeigt also ebenfalls auf die nächste Speicherzelle, und der
Kopiervorgang wiederholt sich aufs Neue. Knirps rollt folglich einer
Dampfwalze gleich mit einer Geschwindigkeit von einer Adresse pro
Ausführunszyklus durch das Feld, eine Spur von "MOV 0, 1" --
Anweisungen hinter sich lassend.
Ein Knirps kann sogar die Seele eines anderen Programmes stehlen:
nämlich dessen Programmzeiger. Wie es das macht, ist leicht
einzusehen. Da ein Knirps mit jedem Ausführungszyklus eine
Speicherzelle nach vorne wandert, ist er im Vergleich zu anderen,
grösseren Programmen ziemlich schnell. Ein Knirps rast von hinten
über ein vor ihm liegendes Programm und überschreibt den Code des
Gegners mit einer unendlichen Folge von "MOV 0, 1" - Befehlen.
Früher oder später wird das derart unterwanderte Programm
wahrscheinlich in den von Knirps überschriebenen Teil
zurückspringen. In diesem Moment wird aus ihm ein neuer Knirps. Es
segelt zwar noch unter der alten Flagge, ist aber fortan dazu verdammt,
dem feindlichen Knirps bis ans Ende der Schlacht willig
hinterherzustapfen.
Spätestens An dieser Stelle sollten Sie einen kleinen Abstecher in
Kapitel zwei machen und sich dort die Einführung zu Core Wars
ansehen. Sie können dann das eben kennengelernte Kampfprogramm Knirps
gegen eines der auf der Programmdiskette enthaltenen grösseren
Kampfprogramme spielen lassen.
1.5.7 ADD und SUB
-----------------
Diese beiden Anweisungen addieren (ADD) oder subtrahieren (SUB) die
Inhalte von zwei Speicherzellen, die durch die beiden Argumente bestimmt
werden. Das erste Argument kann unmittelbar, direkt oder indirekt
angegeben werden, das zweite darf aber nur indirekt oder direkt sein.
Der Wert des ersten Argumentes wird zum Wert des zweiten
Argumentes addiert und das Ergebnis wird an Stelle des zweiten
Argumentes gespeichert: "ADD #5, 1" holt sich den Inhalt der
Speicherzelle, die einen Schritt weiter vorne liegt und zählt 5 dazu.
Das Ergebnis wird dann wieder dort gespeichert, wo das zweite Argument
hinzeigt: einen Schritt vor unserer Anweisung.
"SUB" arbeitet genau gleich, ausser dass anstelle einer Addition
eine Subtraktion erfolgt: Das erste Argument wird vom zweiten abgezogen.
1.5.8 Der allgemeine Sprungbefehl JMP
-------------------------------------
Wenn der Programmzeiger eines Kampfprogrammes zu einer JMP-Anweisung
gelangt, wird das Argument dieser Anweisung einfach zum Wert des
Programmzeigers hinzugezählt. Wieder ein Beispiel:
(5632) MOV #0, 100
(5633) JMP -1
Der Programmzeiger zeigt auf die Anweisung in der Speicherzelle Nr.
5632 und der MOV-Befehl wird ausgeführt. Jetzt wird der Programmzeiger
um eins erhöht und zeigt dadurch auf die nächste Anweisung: "JMP -1".
Durch den JMP-Befehl wird das Argument dieses Befehls zum Programmzeiger
addiert, also $5633+(-1)=5632$. "JMP -1" bewirkt hier einen Sprung um
eine Speicherzelle nach hinten, und die Programmzeile mit der
MOV-Anweisung wird erneut ausgeführt. Diese zwei Zeilen stellen eine
sogenannte "ewige Schleife" dar. Sie kann nicht mehr verlassen werden
und die zwei Befehle werden ausgeführt, bis der Kampf entschieden ist
oder vom Benützer abgebrochen wird.
Die Adressierung des JMP-Befehls ist wieder etwas eingeschränkt:
Es ist nur möglich, die Argumente entweder direkt (wie in unserem
Beispiel) oder indirekt anzugeben. Die unmittelbare Adressierung ist
hier verboten, weil sonst an eine absolute Adresse im Speicherfeld
gesprungen werden könnte.
Sie kennen nun genug Befehle, um das erste grössere Kampfprogramm in
seiner Funktion und in seinem Aufbau zu verstehen. Wenn sie die
folgende Beschreibung durchgelesen haben, können Sie ruhig eine kleine
Verschnaufpause einlegen und Gnom nach Ihren Wünschen abändern.
Natürlich können und sollen Sie Ihre Kreationen immer wieder mit dem
Programm "Core Wars" ausprobieren.
1.5.8.1 Gnom
DAT -1
start ADD #5, -1
MOV #0, @-2
JMP -2
Gnom ist ein sehr dummes, aber auch sehr gefährliches Programm.
Es bombardiert über den ganzen Speicher hinweg jede fünfte Adresse mit
einer Null. Null ist die Zahl, die eine nichtausführbare DAT-Anweisung
anzeigt, und so kann eine ins feindliche Programm geschossene Null
dieses lahmlegen.
Angenommen, Gnom belegt die absoluten Adressen 1 bis 4. Unter Adresse 1
steht anfangs "DAT -1", aber die Ausführung des Programmes beginnt mit
dem nächsten Befehl: "ADD #5, -1". Das Wort "start" anfangs der Zeile
ist ein sogenanntes Schlüsselwort (Symbol). Es darf in einem Programm
nur einmal benützt werden und bestimmt den Startpunkt des Programmes,
also die Zeile, die als erstes ausgeführt wird. Dieser erste Befehl
"ADD #5, -1" bewirkt, dass eine 5 zum Inhalt der vorhergehenden Adresse,
nämlich der Anweisung "DAT -1", addiert wird. Das Ergebnis ist ein "DAT
4" auf der Absolutadresse 1. Als nächstes führt Gnom den Befehl auf der
Absolutadresse 3 aus: "MOV #0, @-2". Darin ist die 0 als unmittelbar
spezifiziert. Sie wird folglich selbst auf eine Zieladresse übertrag
en, die ihrerseits indirekt angegeben ist und sich folgendermassen
errechnet: Zuerst zählt MARS von Adresse 3 zwei Adressen zurück und
landet so bei Adresse 1. Sodann nimmt es den dort stehenden Datenwert,
nämlich 4, und interpretiert ihn als Adresse relativ zur gegenwärtigen
Position. Das heisst, es zählt von Adresse 1 aus 4 Adressen weiter und
legt folglich eine 0 unter Adresse 5 ab.
Die letzte Anweisung von Gnom, "JMP -2", erzeugt eine
Endlosschleife. Sie verlagert die Ausführung zurück zur
Absolutadresse 2. Erneut wird also der DAT-Befehl um 5 erhöht, so
dass er den neuen Wert "DAT 9" annimmt. Im nächsten
Ausführungszyklus wird dem zufolge eine 0 bei der Absolutadresse 10
eingespeichert. Die folgenden 0-Bomben fallen dann auf die Adressen 15,
20, 25 und so weiter.
Das Programm selbst ist unbeweglich, aber seine Artillerie bedroht das
ganze Speicherfeld. Nach etwa 1600 Zyklen schliesslich erreicht Gnom
die Adressen 7990, 7995 und 8000. Für MARS ist 8000 gleichbedeutend
mit 0, und so hat Gnom haarscharf am Selbstmord vorbeigesteuert und sein
nächstes Geschoss landet wieder auf Adresse 5.
"Es ist eine bittere Erkenntnis, dass kein stationäres Programm mit
mehr als vier aufeinanderfolgnden Anweisungen einen Treffer von Gnom
vermeiden kann."
(Aus: Artikel "Computer Kurzweil, Spektrum der Wissenschaft,
Ausgabe vom August 1984, von A. K. Dewdney)
Ein gegnerisches Programm hat nur drei Möglichkeiten: Sich
umherzubewegen, um so dem Bombardement auszuweichen, die Treffer
hinzunehmen und den Schaden zu reparieren oder Gnom als erstes zu
erwischen.
Um mit der letzten Strategie durchzukommen, muss ein Programm schon
Glück haben: Es hat ja keine Ahnung, wo im Speicherfeld Gnom sitzt,
und dieser kann im Mittel 1600 Ausführungszyklen durchlaufen, ehe er
einen Treffer hinnehmen muss. Ist das zweite Programm auch ein Gnom,
gewinnt jedes in etwa 30 Prozent der Fälle, bei 40 Prozent der
Zweikämpfe bekommt dagegen keines der Programme einen tödlichen
Treffer ab.
Rufen Sie sich nun das erste Beispiel, Knirps, wieder in
Erinnerung. Was geschieht, wenn wir Knirps gegen Gnom antreten lassen?
Probieren Sie den Zweikampf ruhig einige Male mit Core Wars aus und
beobachten Sie im Grafikmodus, wie sich die beiden Programme im
Speicher verhalten.
Das Sperrfeuer von Nullen, das Gnom legt, wandert schneller durch das
Speicherfeld als Knirps, aber deswegen ist Gnom nicht unbedingt im
Vorteil. Selbst wenn er Knirps mit seinem Sperrfeuer einholt, muss er
ihn ja erst treffen. Die Quote für einen tödlichen Treffer von Knirps
steht etwa 1 zu fünf gegen die DAT-Bomben von Gnom.
Wenn Knirps umgekehrt Gnom zuerst erreicht, marschiert er
höchstwahrscheinlich schnurstracks durch dessen Code hindurch.
Verlagert Gnoms Anweisung "JMP -2" die Ausführung dann zwei Schritte
zurück, so steht dort die Zeile "MOV 0, 1" von Knirps. Folglich wird
Gnom "umgedreht" und im ein zweites Knirps-Programm verwandelt, das dem
ersten endlos dem Kreis des MARS-Speichers entlang nachjagt. Nach den
Regeln von Core Wars wird das Spiel nach einer bestimmten Anzahl von
Ausführungszyklen abgebrochen und endet in diesem Falle unentschieden.
Es ist noch zu beachten, dass es sich hierbei um das
höchstwahrscheinliche Resultat handelt. Vielleicht analysieren Sie
selbst die anderen Möglichkeiten und entdecken darunter eine, die ein
ganz verrücktes Ergebnis liefert - mehr wird nicht verraten.
1.5.9 Die bedingten Sprünge mit JMZ und JMN
-------------------------------------------
Diese beiden Redcode-Befehle verändern wie der JMP-Befehl den
Programmzeiger und erlauben es dadurch, innerhalb eines Programmes
herumzuspringen. "JMZ" und "JMN" erweitern aber die
Möglichkeiten des einfachen JMP-Befehls: Sie überprüfen zuerst die
Speicherzelle, die durch das zweite Argument bestimmt wird, und
entscheiden dann aufgrund ihres Inhalts, ob gesprungen wird oder nicht.
"JMZ 3, -1" führt den Sprung nur aus, wenn der Inhalt der
Speicherzelle, die eine Adresse weiter hinten im Speicher liegt, genau
Null ist. Wenn der Inhalt grösser oder kleiner Null ist, wird ganz
normal mit der nächsten Anweisung fortgefahren. Das erste Argument des
Befehles gibt die Richtung und die Distanz des Sprunges gleich wie beim
JMP-Befehl an.
Der andere Befehl, "JMN", ist das Gegenteil von "JMZ": Der
Sprung wird nur dann ausgeführt, wenn der Inhalt der durch das zweite
Argument angegebenen Adresse ungleich Null ist.
Durch diese beiden Befehle können anstelle von Endlosschleifen
auch solche Schleifen programmiert werden, die nur eine von vornherein
bestimmte Anzahl von Durchgängen ausgeführt wird. Dazu wird eine
Speicherzelle mit einem DAT-Befehl als Zähler verwendet
und bei jedem Schleifendurchlauf durch die SUB- oder die ADD-Anweisung
erhöht oder vermindert. Der anschliessende JMZ- oder JMN-Befehl
überprüft zuerst die Speicherzelle mit dem Zähler und springt nur an den
Anfang der Schleife zurück, wenn der Zähler die gestellte Bedingung
erfüllt.
1.5.10 Der Vergleichsbefehl CMP
-------------------------------
Mit ihm lassen sich zwei beliebige Speicherzellen miteinander
vergleichen und aufgrund des Ergebnisses bestimmte Massnahmen ergreifen.
Der Inhalt des ersten Argumentes wird mit den Inhalt des zweiten
Argumentes verglichen. Wenn beide Werte gleich sind, wird die
Anweisung, die sofort nach dem CMP-Befehl folgt, übersprungen und die
Ausführung des Programmes erst bei der übernächsten Zeile
fortgesetzt. Wenn die beiden Werte ungleich sind, wird die unmittelbar
folgende Anweisung bearbeitet. Hier ein weiteres Programmbeispiel, das
zwar kein vollständiges Kampfprogramm ist, dafür aber die Anwendung
des Vergleichsbefehls sehr schön aufzeigt:
1.5.10.1 Zwillinge
DAT 0
DAT 99
start MOV @-2, @-1
CMP -3, #9
JMP 4
ADD #1, -5
ADD #1, -5
JMP -5
MOV #99, 93
JMP 93
Unsere ersten beiden Beispiele Knirps und Gnom stehen für eine Klasse
von Programmen, die sich als klein und agressiv, aber nicht als
intelligent klassifizieren lassen. Auf der nächsten Stufe kommen
Programme, die grösser und etwas weniger angriffslustig, dafür aber klug
genug sind, um mit Programmen der niedrigeren Stufe fertigzuwerden. Die
klügeren Programme vermögen einem Angriff dadurch zu entgehen, dass sie
sich selbst aus der Gefahrenzone hinauskopieren.
Jedes dieser Programme hat ein Codesegment wie das eines Programmes
namens Zwillinge. Zwillinge soll kein vollständiges Kampfprogramm sein.
Seine einzige Aufgabe ist es, 100 Adressen weiter vorn eine Kopie von
sich zu erzeugen und dann die Ausführung an die neue Kopie zu
übertragen.
Zwillinge hat drei Hauptkomponenten. Zwei DAT-Anweisungen zu Beginn
dienen als Zeiger: Sie geben an, welche Anweisung als nächstes an
welche Stelle im Speicherfeld kopiert werden soll. Den eigentlichen
Kopiervorgang übernimmt eine Scheife in der Mitte des Programmes; sie
transportiert jeden Befehl zu einer Adresse 100 Schritte hinter seine
aktuellen Position. Bei jedem Passieren der Schleife werden beide
Zeiger um 1 erhöht, so dass sie eine neue Quell- und Zieladresse
bezeichnen. Ein Vergleichsbefehl innerhalb der Schleife prüft den
Wert der ersten DAT-Anweisung. Sobald sie neunmal erhöht wurde, ist
das ganze Programm kopiert, und die Schleife wird verlassen. Nur eine
letzte Korrektur fehlt noch: Die Zieladresse steht in der zweiten
Anweisung des Programmes und hat einen Anfangswert von "DAT 99";
als sie kopiert wurde, war sie jedoch bereits um 1 erhöht, so dass in
der neuen Version des Programmes "DAT 100" steht. Dieser
Uebertragungsfehler wird durch den Befehl "MOV #99, 93"
korrigiert und dann die Ausführung des Programmes durch den letzten
JMP-Befehl an die neue Kopie übergeben.
1.5.11 Die Spaltung von Programmen durch SPL
--------------------------------------------
Dies ist der letzte noch unbekannte Redcode-Befehl. "SPL" kommt
vom englischen Verb 'to split', das auf Deutsch 'spalten' bedeutet. Und
genau das bewirkt dieser Befehl: Wenn der Programmzeiger eines
Redcode-Programmes auf eine SPL-Anweisung trifft, wird das Programm an
zwei Stellen gleichzeitig weitergeführt: Das erste Mal mit der Zeile,
die gleich nach der SPL-Anweisung folgt und das zweite Mal an der
Adresse, die durch das Argument der SPL-Anweisung bestimmt wird.
(0143) -
(0144) MOV #0, 100
(0145) SPL 2
(0146) JMP 101
(0147) -
Dieses Programmfragment zeigt den Gebrauch von "SPL": der
Programmzeiger wird irgendwann auf Zeile 145 treffen. Durch diese Zeile
wird ein neuer Programmzeiger geboren, der seine Tätigkeit in diesem
Fall zwei Zeilen weiter vorne bei Adresse 147 aufnimmt. Gleichzeitig
läuft aber der ursprüngliche Programmzeiger weiter auf Zeile Nummer 146
und springt von dort 101 Programmzeilen weiter nach vorne auf Zeile
$146+101=247$. Dieser Vorgang kann fast beliebig oft wiederholt werden.
So ist es möglich, dass aus einem Anfangsprogramm maximal 63 neue
Programme entstehen, die alle gleichzeitig abgearbeitet werden. Diese
Aufspaltung von Programmen wirft aber einige Probleme auf, wie Sie
gleich sehen werden.
1.6 Der Split-Befehl SPL und seine Wirkung
__________________________________________
1.6.1 Die Ausführung mehrerer Programme
---------------------------------------
Durch den SPL-Befehl erhalten die Redcode-Programmierer die
Möglichkeit, mehrere Programme gleichzeitig unter derselben Flagge
laufen zu lassen. Entsprechend muss festgelegt werden, wie MARS in
diesem Fall die Rechenzeiten verteilt. Das kann auf zweierlei Arten
geschehen.
Angenommen, dem einen Spieler gehörten die Programme $A_1$, $A_2$ und
$A_3$, dem anderen $B_1$ und $B_2$. Eine Möglichkeit wäre, erst
alle Programme des einen und dann die des anderen Spielers laufen zu
lassen. Die Reihenfolge der Ausführung wäre also $A_1$, $A_2$,
$A_3$ und dann $B_1$ und $B_2$. Dieser Zyklus würde sich endlos
wiederholen. Bei der anderen Möglichkeit wechseln sich die Programme
der Spieler ab. Die Reihenfolge wäre dann $A_1$, $B_1$, $A_2$, $B_2$,
$A_3$, $B_1$ und so weiter.
Beide Vorgehensweisen wirken sich ganz unterschiedlich aus. Die
erste Methode begünstigt den, der möglichst viele Programme ins Spiel
bringt, was der Entwicklung intelligenter Strategien wohl kaum
förderlich ist. Die Zweite dagegen bedeutet, dass ein Programm um so
seltener drankommt, je mehr Progrmme ein Spieler laufen hat. Weil sich
der Vorteil einer ungehemmten Programmvermehrung auf diese Weise schnell
aufhebt, gab der Erfinder von MARS diesem Verfahren den Vorzug. Ziel
des Spieles ist es jetzt, alle feindlichen Programme zum Anhalten zu
bringen.
Der Redcode-Programmierer muss sich beim Schreiben eines
Programmes, das sich mehrmals aufteilt, vor Augen halten, dass er zwar
mehrere Programmzeiger gleichzeitig laufen lassen kann, aber dass
dadurch natürlich ein einzelnes seiner Programme viel langsa mer läuft.
Als nächstes wollen wir noch einige Programme behandeln, die die
Anwendung des SPL-Befehls anschaulicher darstellen:
1.6.2 Die Knirps-Kanone
-----------------------
start SPL 2
JMP -1
MOV 0, 1
Was passiert, wenn die erste Zeile dieses Programmes an die Reihe kommt?
Die Anweisung "SPL 2" bedeutet, dass sich das Programm aufteilt und zwei
Programmzeilen ausgeführt werden: "JMP -1" und "MOV 0, 1". Die erste
Instruktion bewirkt einen Rücksprung zur SPL-Anweisung, während die
zweite einen Knirps (erinnern Sie sich? Das erste Programmbeispiel...)
in Marsch setzt. Dieser wälzt sich stur durch den Speicher, denn die
Zieladresse der MOV-Anweisung ist stets die jeweils nächste Adresse.
Mit jedem zweiten Programmzyklus wird also ein neuer Knirps erzeugt, und
so schiebt sich eine Marschkolonne von 63 Knirpsen durch die Kern-Arena
- bereit, jedes feindliche Programm einfach niederzuwalzen.
Auf den ersten Blick scheint gegen ein solches Heer von Knirpsen
keine Verteidigung möglich, aber der Schein trügt:
1.6.3 Das Knirps-Grab
---------------------
MOV #0, -1
JMP -1
Dies ist wiederum nur ein Programmfragment, das zu einem
beliebigen grösseren Kampfprogramm gehören kann, das seine untere Flanke
vor Knirpsen schützen will. Diese zwei Zeilen namens Knirps-Grab
(manchmal auch Knirpsfalle genannt) werden durch eine SPL-Anweisung
innerhalb des grossen Programmes einmal gestartet und arbeiten danach
ohne weitere Unterstützung des grossen Programmes weiter.
Bei jeder Ausführung plaziert Knirps-Grab eine Null direkt vor sich -
in der Hoffnung, einen herannahenden Knirps damit zu treffen. Hier ist
die Anfangs vereinbarte Ausführungsregel entscheidend. Wenn
Kirps-Kanone dem Spieler A gehört und Knirps-Grab dem Spieler B, dann
braucht A $n$ Züge, um $n$ Knirpse eine Zelle weiter nach vorn zu
bewegen; nur einer dieser Knirpse kann auf dem Speicherplatz vor
Knirps-Grab landen. Unter sonst gleichen Bedingungen braucht B sein
Knirps-Grab dagegen nur einmal auszuführen, um einen ankommenden
Knirps zu löschen. Versuchen Sie, eigene Programme zu schreiben, die
das Knirps-Grab verwenden, um sich vor von hinten anrollenden Knirpsen
zu schützen.
1.7 Die Regeln auf einen Blick
______________________________
Das MARS-System besteht aus dem MARS Simulationsprogramm und dem
Redcode-Assembler. Wenn Sie nicht mit dem Texteditor des Programmes
"Core Wars" arbeiten wollen, benötigen Sie darüberhinaus einen
beliebigen Editor oder eine Textverarbeitung, um die Kampfprogramme zu
schreiben. Wenn eine Textverarbeitung verwendet wird, ist es wichtig,
dass sie in der Lage ist, Texte im ASCII-Format abzuspeichern.
Der Redcode-Assembler übersetzt die Programme, die im
ASCII-Format vorliegen müssen, in den für das MARS Simulationsprogramm
ausführbaren Programmcode, der auch Objektcode genannt wird. Man sagt
auch: Der Redcode-Assembler übersetzt den Quelltext in den Objektcode.
Der MARS-Emulator lädt zwei der Kampfprogramme in das Spielfeld, ein
8000 Zellen umfassender Speicher, der von MARS simuliert und verwaltet
wird. Die Positionen, an denen die beiden Kampfprogramme A und B im
Speicherfeld abgelegt werden, werden zufällig ausgewählt, sie
müssen jedoch mindestens 1000 Zellen voneinander entfernt sein.
Nun folgt die Ausführung der beiden Redcode-Programme. Es wird
abwechslungsweise eine Anweisung von Partei A und anschliessend eine
Anweisung von Partei B ausgeführt. Dieser Zyklus wiederholt sich so
lange, bis alle Programme einer Partei zerstört sind oder bis eine
bestimmte Anzahl von Zyklen ausgeführt wurden. Ein Kampfprogramm gilt
als zerstört, wenn es versucht, eine DAT-Anweisung auszuführen.
Dabei ist es egal, ob die DAT-Anweisung von Anfang an an dieser Stelle
stand oder vom Gegner dorthin kopiert wurde.
Der Gewinner des Spiels ist das Programm, das noch läuft (intakt ist),
wenn alle anderen Programme schon zerstört sind. Es gibt zwei
Fälle, in denen ein Kampf unentschieden ausgehen kann: Entweder wird
eine bestimmte (wählbare) Anzahl von Ausführungszyklen
überschritten, oder beide Programme haben zum Zeitpunkt der
Zerstörung genau dieselbe Anzahl gültiger Instruktionen ausgeführt
Das Speicherfeld, in dem der Kampf der Programme stattfindet, ist ein in
sich geschlossener Bereich. Nach der Speicherzelle mit der Adresse 7999
folgt wieder die Adresse 0 und umgekehrt. Die Adressierung der
Redcode-Befehle ist immer relativ zu der augenblicklichen Position des
Programmes im Speicherfeld oder mit anderen Worten relativ zum Inhalt
des Programmzeigers. Ein Redcode-Programm kann zu jeder Zeit den Inhalt
einer beliebigen Speicherzelle (relativ adressiert) im Speicherfeld
erfahren oder einen Sprung zu dieser Speicherzelle ausführen.
Der SPL-Befehl ermöglicht es, dass mehrere Programme für
den gleichen Spieler laufen können. Die Anzahl der gleichzeitig
laufenden Programme ist auf 64 pro Spieler beschränkt. Das ergibt,
sofern beide Spieler dieses Maximum ausnützen, im besten Falle 128
gleichzeitig laufende Programme.
1.8 Erleichterungen
___________________
Der Redcode-Assembler bietet einige Erleichterungen beim Schreiben eines
Kampfprogrammes. Es handelt sich um einen "symbolischen Assembler",
der in der Lage ist, sich bestimmte Adressen unter einem Namen oder eben
dem Symbol zu merken. Dies bringt den Vorteil, dass die Sprungdistanzen
bei den Befehlen SPL, JMP, JMN und JMZ nicht mehr von Hand ausgezählt
werden müssen. Man gibt einfach anstelle der Sprungdistanz in Zahlen
einen Namen ein. Dieser Name wird dann nochmals an den Beginn der
Zeile, zu der gesprungen werden soll, gesetzt. Ein Beispiel ist das
Kampfprogramm Gnom, das schon in einem früheren Abschnitt erklärt
wurde. Hier ist das Programm, so wie es ohne Symbole geschrieben wird:
DAT -1
start ADD #5, -1
MOV #0, @-2
JMP -2
Streng genommen ist das wort "start" auch ein Symbol, aber
dieser Name ist reserviert und zeigt dem Assembler, an welcher Zeile das
Programm später gestartet werden muss. "start" steht immer vor der
als erstes auszuführenden Programmzeile und darf deshalb nur einmal am
Beginn einer Zeile verwendet werden. Ein Programm ohne das
"start"-Symbol wird bei der ersten Zeile im Quelltext gestartet.
Und so sieht das Programm aus, wenn mit Symbolen gearbeitet werden kann:
adresse DAT -1
start ADD #5, adresse
MOV #0, @adresse
JMP start
Der JMP-Befehl in der letzten Zeile springt immer noch um zwei Adressen
zurück, aber anstelle der Zahl -2 steht dort nun das Symbol "start".
Der Assembler berechnet beim Assemblieren des Programmes die Distanz
zwischen dem JMP-Befehl und der Zeile, an deren Anfang das Symbol
"start" geschrieben steht, und fügt dann anstelle von "JMP start" die
Zeile "JMP -2" in das Programm ein. Genau gleich funktioniert das Ganze
bei den beiden vorhergehenden Programmzeilen "ADD #5, adresse" und "MOV
#0, @adresse". Die Befehle beziehen sich immer noch auf die
ursprünglichen Zeilen, aber die Distanzen werden aufgrund der Symbole
vom Assembler ausgerechnet. Wir empfehlen die Verwendung von Symbolen,
denn ein Programm wie Gnom wird durch die verwendeten Namen
verständlicher und leichter lesbar.
Mit Symbolen kann auch gerechnet werden. Man muss dazu wissen, dass
jedes Symbol mit der Zahl in Verbindung gebracht wird, die sich aus der
Numerierung der Programmzeilen bei Null beginnend ergibt. In unserem
Beispiel würde das Symbol "adresse" die Zahl 0 und das Symbol "start"
die Zahl 1 enthalten. Anstelle von "MOV #0, @adresse" könnte auch
geschrieben werden: "MOV #start + adresse - 10, @adresse". Dies
Formulierung ergäbe zwar keinen Sinn, aber es zeigt, dass mit Symbolen
und den Operatoren "+" und "-" bestimmte Werte ausgerechnet werden
können, die dann vom Assembler als Zahlen eingesetzt werden. Der Term
"start + adresse - 10" ergäbe in diesem Fall 1 + 0 - 10 = -9.
Bei der Benützung von Symbolen ist eigentlich nur wichtig, dass jedes
Symbol an irgendeiner Stelle im Programm definiert wird. Ein Symbol
gilt als definiert, wenn es in einer Zeile vor dem Redcode-Befehl
beginnend mit der ersten Zeichen der Zeile auftaucht. Ein Symbol darf
beliebig oft als Argument eines Redcode-Befehls verwendet werden, aber
es darf nur genau einmal pro Programmtext definiert werden.
Es existiert neben "start" noch ein weiteres reserviertes Symbol, das
*-Zeichen. Es bezieht sich immer auf die aktuelle Programmzeile, in der
es sich befindet. Wieder zeigt ein Beispiel die Funktion des *-Symbols:
Diese Version des Programmes Gnom
DAT -1
start ADD #5, -1
MOV #0, @-2
JMP -2
bewirkt genau dasselbe wie diese Version:
DAT -1
start ADD #5, -1 adresse
MOV adresse, @-2
JMP -2
Doch das Ganze kann auch so formuliert werden:
DAT -1
start ADD #5, -1
MOV *, @-2
JMP -2
Das *-Zeichen (Vorsicht: es hat bei Redcode nichts mit einer
Multiplikation zu tun, wie man von anderen Sprachen her vermuten
könnte) wird beim Assemblieren durch den Wert ersetzt, den ein
normales Symbol auch hätte, wenn es in dieser Zeile definiert würde
- nämlich exakt Null. Deshalb können Sie sich eine ausschliesslich
für eine Zeile benützte Definition ersparen, wenn sie den
reservierten Symbolnamen '*' benutzen.
Weitere Informationen, wie Sie einen Programmtext genau
schreiben müssen und welche Beschränkungen Sie dabei berücksichtigen
sollten, finden Sie im zweiten Kapitel dieser Anleitung: Der Abschnitt
2.4 befasst sich mit dem Assembler des Programmes "Core Wars".
1.9 Eine neue Adressierungsart
______________________________
Wir haben Ihnen noch eine letzte Adressierungsart vorenthalten. Sie
kennen die unmittelbare, die direkte und die indirekte Adressierung von
Argumenten für die Redcode-Befehle. Wenn Sie den einen oder andern
dieser drei Modi noch nicht hundertprozentig verstanden haben, sollten
Sie sich nochmals die Abschnitte 1.3 und 1.4 dieses Kapitels vornehmen.
Probieren Sie die dort abgedruckten Beispielprogramme nochmals mit dem
"Core Wars"--Programm aus. Eine gute Hilfe zum Verstehen der
Adressierunsarten ist auch das genaue Verfolgen der disassemblierten
(zurückübersetzten) Programmzeilen eines schwierigen Programmes.
Nähere Informationen finden Sie im Abschnitt 2.8 ("Die
Statusanzeige") im zweiten Kapitel.
Doch nun zurück zu unserer vierten Adressierungsart. Sie nennt sich
"indirekt-dekrement", und wie Sie vielleicht vermuten, handelt
es sich dabei um eine Abwandlung der indirekten Adressierung. Der Name
tönt komplizierter, als sich die Sache in der MARS-Wirklichkeit verhält.
Wir wollen den Vorgang der indirekten Adressierung nochmals
aufwärmen: Das Argument des aktuellen Redcode-Befehls zeigt direkt auf
eine beliebige Adresse. Der Inhalt dieser Adresse wird nun ein zweites
Mal direkt interpretiert und zeigt dadurch auf die entgültige
Adresse, die dann je nach Befehl verwendet oder verändert wird.
Dekrementieren bedeutet, einen Wert um eine bestimmte Zahl zu
vermindern. In der Computertechnologie ist diese Zahl typischerweise
genau eins, so auch bei der indirekt-dekrement Adressierung. Wenn MARS
auf eine Anweisung trifft, deren Argument oder Argumemte auf diese Weise
adressiert sind, laufen folgende Schritte in der angegebenen Reihenfolge
ab:
1. Das betreffende Argument des Redcode-Befehls wird direkt
interpretiert und führt so auf eine Zwischenadresse. Bis jetzt
geschieht genau dasselbe wie bei der indirekten Adressierung.
2. Nun wird dekrementiert: Vom Inhalt dieser Zwischenadresse wird eins
abgezogen (subtrahiert) und das Ergebnis der Rechnung wird wieder
in die Zwischenadresse zurückgespeichert.
3. Ab jetzt läuft alles wieder wie bei der indirekten Adressierung: Der
neue Inhalt der Zwischenadresse wird als direkt angesehen und zeigt
deshalb auf die Endadresse, mit der oder mit deren Inhalt (je nach
Befehl) weitergearbeitet wird. Uff.
Wenn Sie die neue Adressierungsart einigermassen verdaut haben, können
wir Ihnen Abschnitt 3.1 im dritten Kapitel empfehlen. Dort sind weitere
Redcode-Programme aufgelistet und werden etwas auseinandergenommen. Vor
allem das Gewinnerprogramm der ersten Core Wars Weltmeisterschaft im
Jahre 1986 bedient sich ausgiebig der indirekt-dekrement Adressierung.
1.10 Auf den zweiten Blick
__________________________
An dieser Stelle haben Sie nun das Grundwissen eines
Redcode-Programmierers und Sie können behaupten, die Regeln der Sprache zu
beherrschen. Alles, was ab jetzt noch kommt, sollte keine nennenswerten
Schwierigkeiten mehr bieten, weil neue Tatsachen nur noch auf der bis
zu diesem Punkt behandelten Theorie basieren.
Doch wie in jeder Programmiersprache gibt es auch in Redcode
verschiedene Sonderfälle, die viel Zeit und Nerven kosten können, wenn
man sie nicht in ihrer Wirkung kennt. Deshalb haben wir eine Liste der
Eigenheiten von Redcode mit der Erklärung der jeweiligen Falltüren
erstellt.
1.10.1 Unmittelbare Adressierung bei Sprungbefehlen
---------------------------------------------------
Das Ziel eines Sprunges kann, obwohl vier verschiedene
Adressierungsarten bekannt sind, nur durch die drei Modi "direkt",
"indirekt" und "indirekt-inkrement" angegeben werden. Die
unmittelbare Adressierung ist jeweils bei der Sprungadresse der Befehle
"JMP", "JMZ", "JMN", "DJN" und "SPL" nicht erlaubt!
Das ist darin begründet, dass jede Adressierung eine Speicherzelle nie
explizit oder anders ausgedrückt nie direkt mit ihrer wirklichen
Nummer erreichen kann. Eine andere Speicherzelle wird bei Redcode immer
relativ zur augenblicklichen Position im Speicher angesprochen. Dadurch
wird verhindert dass
a) ein Redcode-Programm seine eigene Position im Speicherfeld erfahren
kann und dass
b) alle Redcode-Programme lauffähig sind, egal an welche Position des
Speichers sie zu Beginn eines Kampfes geladen wurden.
1.10.2 Unmittelbare Adressierung bei MOV, ADD, SUB und DJN
----------------------------------------------------------
Auf den ersten Blick haben diese vier Befehle nicht viel gemeinsam, aber
dennoch gehören sie zusammen unter diesen Titel: Alle vier verändern den
Inhalt einer beliebigen Speicherzelle. "ADD" und "SUB" führen mit der
durch das zweite Argument bestimmten Speicherzelle eine Subtraktion oder
Addition durch. "MOV" kopiert den Inhalt einer Adresse in eine andere
Adresse, die durch das zweite Argument angegeben wird. Der alte Inhalt
dieser Zieladresse geht dabei verloren, sie wird also ebenfalls
verändert. Der vierte im Bunde ist der Befehl "DJN", der eine
Speicherzelle um eins vermindert und dann den neuen Inhalt dieser Zelle
mit Null vergleicht.
Bei den genannten Befehlen ist es ebenfalls nicht erlaubt, bei der
Angabe der Zieladresse den Modus unmittelbar zu verwenden. Die Gründe
dafür sind dieselben wie bei den Sprungbefehlen: Ein Redcode-Programm
darf seine Position im Speicher nicht erfahren und soll an jeder Stelle
im Speicherfeld lauffähig sein.
1.10.3 Der Vergleichsbefehl CMP
-------------------------------
Bei seiner Anwendung muss beachtet werden, dass entweder ganze
Speicherzellen oder nur einzelne Werte verglichen werden können. Wir
rufen uns in Erinnerung, dass eine MARS-Speicherzelle einen
vollständigen Redcode-Befehl, also das Befehlswort mit seinen maximal
zwei Argumenten und Adressierungsmodi, aufnehmen kann. Ob nun nur zwei
Werte oder gleich eine ganze Speicherzelle verglichen wird, hängt von
der verwendeten Adressierungsart ab.
Wenn weder das erste noch das zweite Argument des CMP-Befehls
unmittelbar adressiert ist, weisen die Argumente auf "ganze"
Speicherzellen. Somit werden beide Zellen, die durch die Argumente
bestimmt sind, vollständig verglichen. Vollständig bedeutet, dass
sowohl der Befehl, als auch die Argumentwerte und die Adressierungsmodi
verglichen werden. Der Vergleich ist nur dann wahr, wenn wirklich alle
diese Punkte der beiden Befehle übereinstimmen. Der Verlgeich ist
aber falsch, sobald auch nur einer der Punkte der einen Adresse von dem
entsprechenden Punkt der anderen Adresse abweicht.
Einzelne Werte dagegen werden verglichen, wenn eines der beiden
Argumente eine unmittelbare Zahl ist, z.B. #8. Normalerweise ist
nur eines der Argumente unmittelbar, weil ein Vergleich der beiden
Zahlen #3 und #9 von vornherein bekannt ist und deshalb keinen Sinn
ergibt. Das andere Argument ist also direkt, indirekt oder
indirekt-dekrement adressiert und stellt eine Ziel- oder
Vergleichsadresse dar. In dieser Adresse wird der Wert, der das zweite
(B-) Argument darstellt, mit dem unmittelbaren Wert des CMP-Befehls
verglichen. In diesem Fall werden folglich nur zwei Zahlen verglichen:
Das B-Argument der Zieladresse und die unmittelbare Zahl in der
CMP-Anweisung.
1.10.4 DAT-Befehle mit dem MOV-Befehl kopieren
----------------------------------------------
Der MOV-Befehl dient in erster Linie dazu, ganze Programmzeilen
an eine andere Stelle im Speicherfeld zu kopieren oder bestimmte Zahlen
als DAT-Befehle im Speicher abzulegen.
Wenn in einer MOV-Anweisung die Quelladresse (das erste Argument)
unmittelbar angegeben ist, wird an die Zieladresse (das zweite Argument)
ein DAT-Befehl kopiert. Dieser DAT-Befehl besitzt den Wert, der durch
die Zieladresse unmittelbar angegeben ist. Die Anweisung "MOV #8, 10"
kopiert deshalb einen "DAT 8"-Befehl auf die Zieladresse, die 10 Zeilen
weiter hinten im Speicherfeld liegt.
Ist jedoch die Quelladresse direkt, indirekt oder indirekt-inkrement
adressiert, wird die ganze Programmzeile, die durch die Quelladresse
bestimmt wird, in die Speicherzelle kopiert, die durch die Zieladresse
des MOV-Befehls angegeben wird.
Wichtig bei der MOV-Anweisung ist, dass Sie wissen, in welchen Fällen
neue DAT-Befehle geschaffen und wann ganze Programmzeilen kopiert
werden.
1.10.5 Die Anwendung von ADD und SUB
------------------------------------
Diese Befehle dienen zum Addieren und Subtrahieren in Speicherzellen.
Diese Operationen können sowohl auf Zahlen (unmittelbar) als auch
Programmzeilen (direkt, indirekt und indirekt-inkrement) angewandt
werden. Beachten Sie aber, dass sich die beiden Befehle je nach
Anweisung unterschiedlich verhalten können.
Jede Zelle im MARS-Speicherfeld besteht aus einer Anweisung und zwei
Argumenten. Es spielt dabei keine Rolle, ob der enthaltene Befehl zwei
oder nur ein Argument benötigt: Im Speicher belegt er eine ganze
Zeile. Wenn zwei Zeilen addiert oder subtrahiert werden sollen, erfolgt
die Operation in zwei Schritten: Zuerst wird Argument A der ersten Zeile
mit Argument A der zweiten Zeile verrechnet, anschliessend geschieht
dasselbe mit den B-Argumenten. Das heisst, dass die Addition einer
DAT-Zeile zu einem Sprungbefehl sinnlos ist, weil die Anweisung DAT das
Argument B verwendet, die Anweisung "JMP" (springen) jedoch das
Argument A. Wenn der Wert eines Befehls Unmittelbar (#) angegeben
wird, bezieht sich diese Zahl auf beide Argumente. Die drei folgenden
Programmfragmente liefern sinnvolle Werte:
DAT 8
ADD #1, -1
Addieren von 1 zu der DAT-Anweisung hinter dem ADD-Befehl: Nach der
Ausführung enthält die Zeile "DAT 9".
DAT 15
DAT 6
SUB -1, -2
"SUB -1, -2" subtrahiert von "DAT 15" den Wert 6, der in der anderen
DAT-Zeile enthalten ist. "DAT 15" wird danach zu "DAT 9".
CMP 5, -100
ADD #1, -1
Hier wird zur gesamten CMP-Befehlszeile oder mit anderen Worten zu
beiden Argumenten der Wert eins addiert, weil dieser unmittelbar
adressiert ist. Anschliessend würde also die Zeile, die 6 Schritte
($5+1$) nach CMP steht, mit der Zeile verglichen, die sich 99 ($-100+1$)
Schritte vor der CMP-Anweisung befindet. Schliesslich noch eine
schlechte Anwendung von "ADD":
DAT 10
JMP -30
ADD -2, -1
Hier bezieht sich der ADD-Befehl auf die Anweisung "JMP -30" und bleibt
ohne Wirkung, weil "DAT 10" keinen sinnvollen Wert zur Addition liefern
kann: "JMP" benötigt das Argument A, "DAT" dagegen das Argument B.
1.11 Turniere
_____________
Core Wars stand im Herbst der Jahres 1986 im Rampenlicht des
Computermuseums in Boston. Dort fand das erste internationale
Core Wars -- Turnier statt. Von den 31 teilnehmenden Programmen
erwiesen sich drei als äusserst robust. Sieger wurde schliesslich ein
Programm namens 'mice', in Englisch die Mehrzahl von Maus, also Mäuse.
Der erste Preis bestand übrigens in einer Trophäe mit einer
Kernspeicher-Karte aus einem alten CDC-6600 - Computer.
Das Turnier war darauf angelegt, möglichst viele Begegnungen
stattfinden zu lassen. Aber ein vollständiger Kampf zwischen den 31
Teilnehmern, jeder gegen jeden, hätte 465 Schlachten erfordert, und
soviel Zeit stand nicht zur Verfügung. Die Teilnehmer wurden daher
willkürlich in zwei nahezu gleich grosse Gruppen aufgeteilt. Innerhalb
der Gruppen trat dann jeder gegen jeden an.
Die vier bestplatzierten Programme von jeder Gruppe kämpften
dann wieder jedes gegen jedes. Von diesen insgesamt acht Programmen
kamen drei ins Finale, aus dem dann der endgültige Sieger des Turniers
hervorging, eben "Mice".
Dieses Siegerprogramm und ein Programm namens "Chang", das den zweiten
Platz erringen konnte, werden im dritten Kapitel vorgestellt und
erklärt. Neu hinzugekommen ist das Kampfprogramm "Mausefalle",
welches "Mice" in den meisten Fällen schlagen kann.
Im Dezember des Jahres 1988 findet übrigens wiederum ein Core Wars
Turnier statt, von dem aber die Resultate und die Siegerprogramme noch
nicht bekannt sind.